#!/usr/bin/python
#
# Copyright (C) 2009 Tan Swee Heng
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Classes to interact with the Google Finance server."""


__author__ = 'thesweeheng@gmail.com'


import gdata.service
import gdata.finance
import atom


class PortfolioQuery(gdata.service.Query):
    """A query object for the list of a user's portfolios."""

    def returns(self):
        return self.get('returns', False)

    def set_returns(self, value):
        if value is 'true' or value is True:
            self['returns'] = 'true'

    returns = property(returns, set_returns, doc="The returns query parameter")

    def positions(self):
        return self.get('positions', False)

    def set_positions(self, value):
        if value is 'true' or value is True:
            self['positions'] = 'true'

    positions = property(positions, set_positions,
        doc="The positions query parameter")


class PositionQuery(gdata.service.Query):
    """A query object for the list of a user's positions in a portfolio."""

    def returns(self):
        return self.get('returns', False)

    def set_returns(self, value):
        if value is 'true' or value is True:
            self['returns'] = 'true'

    returns = property(returns, set_returns,
        doc="The returns query parameter")

    def transactions(self):
        return self.get('transactions', False)

    def set_transactions(self, value):
        if value is 'true' or value is True:
            self['transactions'] = 'true'

    transactions = property(transactions, set_transactions,
        doc="The transactions query parameter")


class FinanceService(gdata.service.GDataService):

    def __init__(self, email=None, password=None, source=None,
                 server='finance.google.com', **kwargs):
        """Creates a client for the Finance service.

        Args:
          email: string (optional) The user's email address, used for
              authentication.
          password: string (optional) The user's password.
          source: string (optional) The name of the user's application.
          server: string (optional) The name of the server to which a connection
              will be opened. Default value: 'finance.google.com'.
          **kwargs: The other parameters to pass to gdata.service.GDataService
              constructor.
        """
        gdata.service.GDataService.__init__(self,
            email=email, password=password, service='finance', server=server,
            **kwargs)

    def GetPortfolioFeed(self, query=None):
        uri = '/finance/feeds/default/portfolios'
        if query:
            uri = PortfolioQuery(feed=uri, params=query).ToUri()
        return self.Get(uri, converter=gdata.finance.PortfolioFeedFromString)

    def GetPositionFeed(self, portfolio_entry=None, portfolio_id=None,
        query=None):
        """
        Args:
          portfolio_entry: PortfolioEntry (optional; see Notes)
          portfolio_id: string (optional; see Notes) This may be obtained
              from a PortfolioEntry's portfolio_id attribute.
          query: PortfolioQuery (optional)

        Notes:
          Either a PortfolioEntry OR a portfolio ID must be provided.
        """
        if portfolio_entry:
            uri = portfolio_entry.GetSelfLink().href + '/positions'
        elif portfolio_id:
            uri = '/finance/feeds/default/portfolios/%s/positions' % portfolio_id
        if query:
            uri = PositionQuery(feed=uri, params=query).ToUri()
        return self.Get(uri, converter=gdata.finance.PositionFeedFromString)

    def GetTransactionFeed(self, position_entry=None,
        portfolio_id=None, ticker_id=None):
        """
        Args:
          position_entry: PositionEntry (optional; see Notes)
          portfolio_id: string (optional; see Notes) This may be obtained
              from a PortfolioEntry's portfolio_id attribute.
          ticker_id: string (optional; see Notes) This may be obtained from
              a PositionEntry's ticker_id attribute. Alternatively it can
              be constructed using the security's exchange and symbol,
              e.g. 'NASDAQ:GOOG'

        Notes:
          Either a PositionEntry OR (a portfolio ID AND ticker ID) must
          be provided.
        """
        if position_entry:
            uri = position_entry.GetSelfLink().href + '/transactions'
        elif portfolio_id and ticker_id:
            uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \
                % (portfolio_id, ticker_id)
        return self.Get(uri, converter=gdata.finance.TransactionFeedFromString)

    def GetPortfolio(self, portfolio_id=None, query=None):
        uri = '/finance/feeds/default/portfolios/%s' % portfolio_id
        if query:
            uri = PortfolioQuery(feed=uri, params=query).ToUri()
        return self.Get(uri, converter=gdata.finance.PortfolioEntryFromString)

    def AddPortfolio(self, portfolio_entry=None):
        uri = '/finance/feeds/default/portfolios'
        return self.Post(portfolio_entry, uri,
            converter=gdata.finance.PortfolioEntryFromString)

    def UpdatePortfolio(self, portfolio_entry=None):
        uri = portfolio_entry.GetEditLink().href
        return self.Put(portfolio_entry, uri,
            converter=gdata.finance.PortfolioEntryFromString)

    def DeletePortfolio(self, portfolio_entry=None):
        uri = portfolio_entry.GetEditLink().href
        return self.Delete(uri)

    def GetPosition(self, portfolio_id=None, ticker_id=None, query=None):
        uri = '/finance/feeds/default/portfolios/%s/positions/%s' \
            % (portfolio_id, ticker_id)
        if query:
            uri = PositionQuery(feed=uri, params=query).ToUri()
        return self.Get(uri, converter=gdata.finance.PositionEntryFromString)

    def DeletePosition(self, position_entry=None,
        portfolio_id=None, ticker_id=None, transaction_feed=None):
        """A position is deleted by deleting all its transactions.

        Args:
          position_entry: PositionEntry (optional; see Notes)
          portfolio_id: string (optional; see Notes) This may be obtained
              from a PortfolioEntry's portfolio_id attribute.
          ticker_id: string (optional; see Notes) This may be obtained from
              a PositionEntry's ticker_id attribute. Alternatively it can
              be constructed using the security's exchange and symbol,
              e.g. 'NASDAQ:GOOG'
          transaction_feed: TransactionFeed (optional; see Notes)

        Notes:
          Either a PositionEntry OR (a portfolio ID AND ticker ID) OR
          a TransactionFeed must be provided.
        """
        if transaction_feed:
            feed = transaction_feed
        else:
            if position_entry:
                feed = self.GetTransactionFeed(position_entry=position_entry)
            elif portfolio_id and ticker_id:
                feed = self.GetTransactionFeed(
                    portfolio_id=portfolio_id, ticker_id=ticker_id)
        for txn in feed.entry:
            self.DeleteTransaction(txn)
        return True

    def GetTransaction(self, portfolio_id=None, ticker_id=None,
        transaction_id=None):
        uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions/%s' \
            % (portfolio_id, ticker_id, transaction_id)
        return self.Get(uri, converter=gdata.finance.TransactionEntryFromString)

    def AddTransaction(self, transaction_entry=None, transaction_feed = None,
        position_entry=None, portfolio_id=None, ticker_id=None):
        """
        Args:
          transaction_entry: TransactionEntry (required)
          transaction_feed: TransactionFeed (optional; see Notes)
          position_entry: PositionEntry (optional; see Notes)
          portfolio_id: string (optional; see Notes) This may be obtained
              from a PortfolioEntry's portfolio_id attribute.
          ticker_id: string (optional; see Notes) This may be obtained from
              a PositionEntry's ticker_id attribute. Alternatively it can
              be constructed using the security's exchange and symbol,
              e.g. 'NASDAQ:GOOG'

        Notes:
          Either a TransactionFeed OR a PositionEntry OR (a portfolio ID AND
          ticker ID) must be provided.
        """
        if transaction_feed:
            uri = transaction_feed.GetPostLink().href
        elif position_entry:
            uri = position_entry.GetSelfLink().href + '/transactions'
        elif portfolio_id and ticker_id:
            uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \
                % (portfolio_id, ticker_id)
        return self.Post(transaction_entry, uri,
            converter=gdata.finance.TransactionEntryFromString)

    def UpdateTransaction(self, transaction_entry=None):
        uri = transaction_entry.GetEditLink().href
        return self.Put(transaction_entry, uri,
            converter=gdata.finance.TransactionEntryFromString)

    def DeleteTransaction(self, transaction_entry=None):
        uri = transaction_entry.GetEditLink().href
        return self.Delete(uri)
